// 版权所有2009年围棋作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package fmt

import (
	"strconv"
	"unicode/utf8"
)

const (
	ldigits = "0123456789abcdefx"
	udigits = "0123456789ABCDEFX"
)

const (
	signed   = true
	unsigned = false
)

// 标记放置在单独的结构中，便于清除。
type fmtFlags struct {
	widPresent  bool
	precPresent bool
	minus       bool
	plus        bool
	sharp       bool
	space       bool
	zero        bool

	// 对于%+v%#v格式，我们设置了plusV/sharpV标志
	// 并清除正/锐标志，因为%+v和%#v有效
	// 在顶层设置不同的无标志格式。
	plusV  bool
	sharpV bool
}

// fmt是Printf等使用的原始格式化程序。
// 它打印到必须单独设置的缓冲区中。
type fmt struct {
	buf *buffer

	fmtFlags

	wid  int // 宽度
	prec int // 精确

	// intbuf足够大，可以存储带有符号和
	// 避免在32位体系结构的结构末尾填充。
	intbuf [68]byte
}

func (f *fmt) clearflags() {
	f.fmtFlags = fmtFlags{}
}

func (f *fmt) init(buf *buffer) {
	f.buf = buf
	f.clearflags()
}

// writePadding生成n字节的填充。
func (f *fmt) writePadding(n int) {
	if n <= 0 { // 不需要填充字节。
		return
	}
	buf := *f.buf
	oldLen := len(buf)
	newLen := oldLen + n
	// 给垫子留出足够的空间。
	if newLen > cap(buf) {
		buf = make(buffer, cap(buf)*2+n)
		copy(buf, *f.buf)
	}
	// 决定填充的字节。
	padByte := byte(' ')
	if f.zero {
		padByte = byte('0')
	}
	// 用padByte填充填充。
	padding := buf[oldLen:newLen]
	for i := range padding {
		padding[i] = padByte
	}
	*f.buf = buf[:newLen]
}

// pad将b附加到f.buf，在左侧（！f.minus）或右侧（f.minus）填充。
func (f *fmt) pad(b []byte) {
	if !f.widPresent || f.wid == 0 {
		f.buf.write(b)
		return
	}
	width := f.wid - utf8.RuneCount(b)
	if !f.minus {
		// 左填充
		f.writePadding(width)
		f.buf.write(b)
	} else {
		// 右填充
		f.buf.write(b)
		f.writePadding(width)
	}
}

// padString附加到f.buf，在左侧（！f.minus）或右侧（f.minus）填充。
func (f *fmt) padString(s string) {
	if !f.widPresent || f.wid == 0 {
		f.buf.writeString(s)
		return
	}
	width := f.wid - utf8.RuneCountInString(s)
	if !f.minus {
		// 左填充
		f.writePadding(width)
		f.buf.writeString(s)
	} else {
		// 右填充
		f.buf.writeString(s)
		f.writePadding(width)
	}
}

// fmtBoolean设置布尔值的格式。
func (f *fmt) fmtBoolean(v bool) {
	if v {
		f.padString("true")
	} else {
		f.padString("false")
	}
}

// fmtUnicode将uint64格式化为“U+0078”或将f.sharp设置为“U+0078‘x’”。
func (f *fmt) fmtUnicode(u uint64) {
	buf := f.intbuf[0:]

	// 默认精度设置时，所需的最大buf长度为18
	// 用于设置-1的格式，其中包含%#U（“U+ffffffffffff”），适合
	// 进入已分配的intbuf，容量为68字节。
	prec := 4
	if f.precPresent && f.prec > 4 {
		prec = f.prec
		// 计算“U+”、数字“”、字符“””所需的空间。
		width := 2 + prec + 2 + utf8.UTFMax + 1
		if width > len(buf) {
			buf = make([]byte, width)
		}
	}

	// 格式为buf，以buf[i]结尾。从右到左格式化数字更容易。
	i := len(buf)

	// 对于%#U，我们希望在缓冲区的末尾添加空格和带引号的字符。
	if f.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) {
		i--
		buf[i] = '\''
		i -= utf8.RuneLen(rune(u))
		utf8.EncodeRune(buf[i:], rune(u))
		i--
		buf[i] = '\''
		i--
		buf[i] = ' '
	}
	// 将Unicode代码点u格式化为十六进制数。
	for u >= 16 {
		i--
		buf[i] = udigits[u&0xF]
		prec--
		u >>= 4
	}
	i--
	buf[i] = udigits[u]
	prec--
	// 在数字前面加零，直到达到要求的精度。
	for prec > 0 {
		i--
		buf[i] = '0'
		prec--
	}
	// 添加一个前导“U+”。
	i--
	buf[i] = '+'
	i--
	buf[i] = 'U'

	oldZero := f.zero
	f.zero = false
	f.pad(buf[i:])
	f.zero = oldZero
}

// fmtInteger格式化有符号和无符号整数。
func (f *fmt) fmtInteger(u uint64, base int, isSigned bool, verb rune, digits string) {
	negative := isSigned && int64(u) < 0
	if negative {
		u = -u
	}

	buf := f.intbuf[0:]
	// 已分配容量为68字节的f.intbuf
	// 当未设置精度或宽度时，足够大以进行整数格式设置。
	if f.widPresent || f.precPresent {
		// 考虑可能添加符号和“0x”的3个额外字节。
		width := 3 + f.wid + f.prec // wid和prec始终为正值。
		if width > len(buf) {
			// 我们需要一艘更大的船。
			buf = make([]byte, width)
		}
	}

	// 有两种方法可以请求额外的前导零位：%.3d或%03d。
	// 如果两者都指定，则忽略f.zero标志，并
	// 改为使用空格填充。
	prec := 0
	if f.precPresent {
		prec = f.prec
		// 精度为0，值为0表示“不打印”，只打印填充。
		if prec == 0 && u == 0 {
			oldZero := f.zero
			f.zero = false
			f.writePadding(f.wid)
			f.zero = oldZero
			return
		}
	} else if f.zero && f.widPresent {
		prec = f.wid
		if negative || f.plus || f.space {
			prec-- // 留有余地
		}
	}

	// 因为从右到左打印更容易：将u格式化为buf，以buf[i]结尾。
	// 我们可以通过拆分32位的大小写使事情稍微快一点
	// 但它不值得复制，所以u有64位。
	i := len(buf)
	// 使用常量进行除法和模运算以获得更高效的代码。
	// 开关箱按流行程度排序。
	switch base {
	case 10:
		for u >= 10 {
			i--
			next := u / 10
			buf[i] = byte('0' + u - next*10)
			u = next
		}
	case 16:
		for u >= 16 {
			i--
			buf[i] = digits[u&0xF]
			u >>= 4
		}
	case 8:
		for u >= 8 {
			i--
			buf[i] = byte('0' + u&7)
			u >>= 3
		}
	case 2:
		for u >= 2 {
			i--
			buf[i] = byte('0' + u&1)
			u >>= 1
		}
	default:
		panic("fmt: unknown base; can't happen")
	}
	i--
	buf[i] = digits[u]
	for i > 0 && prec > len(buf)-i {
		i--
		buf[i] = '0'
	}

	// 各种前缀：0x、，等等。
	if f.sharp {
		switch base {
		case 2:
			// 添加前导0b。
			i--
			buf[i] = 'b'
			i--
			buf[i] = '0'
		case 8:
			if buf[i] != '0' {
				i--
				buf[i] = '0'
			}
		case 16:
			// 添加前导0x或0x。
			i--
			buf[i] = digits[16]
			i--
			buf[i] = '0'
		}
	}
	if verb == 'O' {
		i--
		buf[i] = 'o'
		i--
		buf[i] = '0'
	}

	if negative {
		i--
		buf[i] = '-'
	} else if f.plus {
		i--
		buf[i] = '+'
	} else if f.space {
		i--
		buf[i] = ' '
	}

	// 用零填充的左边已经像前面一样处理过了
	// 或者，由于显式设置的精度，f.zero标志被忽略。
	oldZero := f.zero
	f.zero = false
	f.pad(buf[i:])
	f.zero = oldZero
}

// truncateString将字符串s截断到指定的精度（如果存在）。
func (f *fmt) truncateString(s string) string {
	if f.precPresent {
		n := f.prec
		for i := range s {
			n--
			if n < 0 {
				return s[:i]
			}
		}
	}
	return s
}

// truncate将字节片b截断为指定精度的字符串（如果存在）。
func (f *fmt) truncate(b []byte) []byte {
	if f.precPresent {
		n := f.prec
		for i := 0; i < len(b); {
			n--
			if n < 0 {
				return b[:i]
			}
			wid := 1
			if b[i] >= utf8.RuneSelf {
				_, wid = utf8.DecodeRune(b[i:])
			}
			i += wid
		}
	}
	return b
}

// fmtS格式化字符串。
func (f *fmt) fmtS(s string) {
	s = f.truncateString(s)
	f.padString(s)
}

// fmtBs格式化字节片b，就好像它是用fmtS格式化为字符串一样。
func (f *fmt) fmtBs(b []byte) {
	b = f.truncate(b)
	f.pad(b)
}

// fmtSbx将字符串或字节片格式化为其字节的十六进制编码。
func (f *fmt) fmtSbx(s string, b []byte, digits string) {
	length := len(b)
	if b == nil {
		// 不存在字节片。假设应该对字符串s进行编码。
		length = len(s)
	}
	// 将长度设置为处理的字节数不超过精度要求。
	if f.precPresent && f.prec < length {
		length = f.prec
	}
	// 计算编码宽度时考虑f.sharp和f.space标志。
	width := 2 * length
	if width > 0 {
		if f.space {
			// 由两个十六进制编码的每个元素将得到一个前导0x或0x。
			if f.sharp {
				width *= 2
			}
			// 元素将由一个空格分隔。
			width += length - 1
		} else if f.sharp {
			// 只为整个字符串添加前导0x或0x。
			width += 2
		}
	} else { // 应编码的字节片或字符串为空。
		if f.widPresent {
			f.writePadding(f.wid)
		}
		return
	}
	// 把手放在左边。
	if f.widPresent && f.wid > width && !f.minus {
		f.writePadding(f.wid - width)
	}
	// 将编码直接写入输出缓冲区。
	buf := *f.buf
	if f.sharp {
		// 添加前导0x或0x。
		buf = append(buf, '0', digits[16])
	}
	var c byte
	for i := 0; i < length; i++ {
		if f.space && i > 0 {
			// 用空格分隔元素。
			buf = append(buf, ' ')
			if f.sharp {
				// 为每个元素添加前导0x或0x。
				buf = append(buf, '0', digits[16])
			}
		}
		if b != nil {
			c = b[i] // 从输入字节片中获取一个字节。
		} else {
			c = s[i] // 从输入字符串中提取一个字节。
		}
		// 将每个字节编码为两个十六进制数字。
		buf = append(buf, digits[c>>4], digits[c&0xF])
	}
	*f.buf = buf
	// 向右处理填充。
	if f.widPresent && f.wid > width && f.minus {
		f.writePadding(f.wid - width)
	}
}

// fmtSx将字符串格式化为其字节的十六进制编码。
func (f *fmt) fmtSx(s, digits string) {
	f.fmtSbx(s, nil, digits)
}

// fmtBx将字节片格式化为其字节的十六进制编码。
func (f *fmt) fmtBx(b []byte, digits string) {
	f.fmtSbx("", b, digits)
}

// fmtQ将字符串格式化为双引号转义Go字符串常量。
// 如果设置了f.sharp，则可能会返回原始（反引号）字符串
// 如果字符串不包含除制表符以外的任何控制字符。
func (f *fmt) fmtQ(s string) {
	s = f.truncateString(s)
	if f.sharp && strconv.CanBackquote(s) {
		f.padString("`" + s + "`")
		return
	}
	buf := f.intbuf[:0]
	if f.plus {
		f.pad(strconv.AppendQuoteToASCII(buf, s))
	} else {
		f.pad(strconv.AppendQuote(buf, s))
	}
}

// fmtC将整数格式化为Unicode字符。
// 如果字符不是有效的Unicode，它将打印“\ufffd”。
func (f *fmt) fmtC(c uint64) {
	r := rune(c)
	if c > utf8.MaxRune {
		r = utf8.RuneError
	}
	buf := f.intbuf[:0]
	w := utf8.EncodeRune(buf[:utf8.UTFMax], r)
	f.pad(buf[:w])
}

// fmtQc将整数格式化为单引号转义Go字符常量。
// 如果字符不是有效的Unicode，它将打印“\ufffd”。
func (f *fmt) fmtQc(c uint64) {
	r := rune(c)
	if c > utf8.MaxRune {
		r = utf8.RuneError
	}
	buf := f.intbuf[:0]
	if f.plus {
		f.pad(strconv.AppendQuoteRuneToASCII(buf, r))
	} else {
		f.pad(strconv.AppendQuoteRune(buf, r))
	}
}

// fmtFloat格式化浮点64。它假定谓词是有效的格式说明符
// 对于strconv.AppendFloat，因此适合于字节。
func (f *fmt) fmtFloat(v float64, size int, verb rune, prec int) {
	// 格式说明符中的显式精度将否决默认精度。
	if f.precPresent {
		prec = f.prec
	}
	// 格式化数字，如果需要，为前导+符号保留空间。
	num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size)
	if num[1] == '-' || num[1] == '+' {
		num = num[1:]
	} else {
		num[0] = '+'
	}
	// f、 空格表示添加前导空格而不是“+”号，除非
	// f.plus明确要求使用该符号。
	if f.space && num[0] == '+' && !f.plus {
		num[0] = ' '
	}
	// 对无穷大和NaN的特殊处理，
	// 看起来不像数字，所以不应该用零填充。
	if num[1] == 'I' || num[1] == 'N' {
		oldZero := f.zero
		f.zero = false
		// 如果未要求，请删除NaN前的标志。
		if num[1] == 'N' && !f.space && !f.plus {
			num = num[1:]
		}
		f.pad(num)
		f.zero = oldZero
		return
	}
	// 对于非二进制格式，夏普标志强制打印小数点
	// 并保留尾随的零，我们可能需要恢复这些零。
	if f.sharp && verb != 'b' {
		digits := 0
		switch verb {
		case 'v', 'g', 'G', 'x':
			digits = prec
			// 如果未明确设置精度，请使用精度6。
			if digits == -1 {
				digits = 6
			}
		}

		// 缓冲区预先分配有足够的存储空间
		// “e+123”或“p-1023”形式的指数符号。
		var tailBuf [6]byte
		tail := tailBuf[:0]

		hasDecimalPoint := false
		sawNonzeroDigit := false
		// 从i=1开始跳过num[0]处的符号。
		for i := 1; i < len(num); i++ {
			switch num[i] {
			case '.':
				hasDecimalPoint = true
			case 'p', 'P':
				tail = append(tail, num[i:]...)
				num = num[:i]
			case 'e', 'E':
				if verb != 'x' && verb != 'X' {
					tail = append(tail, num[i:]...)
					num = num[:i]
					break
				}
				fallthrough
			default:
				if num[i] != '0' {
					sawNonzeroDigit = true
				}
				// 在第一个非零数字之后计算有效数字。
				if sawNonzeroDigit {
					digits--
				}
			}
		}
		if !hasDecimalPoint {
			// 前导数字0应为数字贡献一次。
			if len(num) == 2 && num[1] == '0' {
				digits--
			}
			num = append(num, '.')
		}
		for digits > 0 {
			num = append(num, '0')
			digits--
		}
		num = append(num, tail...)
	}
	// 如果有人要求，我们想要一个信号，如果信号不是正面的。
	if f.plus || num[0] != '+' {
		// 如果我们在左边补零，我们希望符号在前导零之前。
		// 为此，请写入注销，然后填充未签名的数字。
		if f.zero && f.widPresent && f.wid > len(num) {
			f.buf.writeByte(num[0])
			f.writePadding(f.wid - len(num))
			f.buf.write(num[1:])
			return
		}
		f.pad(num)
		return
	}
	// 无迹象显示，数字为正数；只需打印未签名的数字。
	f.pad(num[1:])
}
